import Node from "./node";
import { getNodeKey } from "./util";

export default class TreeStore {
  constructor(options) {
    this.currentNode = null;
    this.nodesMap = {};
    for (let option in options) {
      this[option] = options[option];
    }
    this.root = new Node({
      data: this.data,
      store: this
    });
    if (this.lazy && this.load) {
      const loadFn = this.load;
      loadFn(this.root, data => {
        this.root.createNodeChildren(data);
        this._initDefaultCheckedNodes();
      });
    } else {
      this._initDefaultCheckedNodes();
    }
  }
  registerNode(node) {
    const key = this.key;
    if (!key || !node || !node.data) return;
    const nodeKey = node.key;
    if (nodeKey !== undefined) this.nodesMap[node.key] = node;
  }
  deregisterNode(node) {
    const key = this.key;
    if (!key || !node || !node.data) return;

    const childNodes = node.childNodes;
    for (let i = 0, j = childNodes.length; i < j; i++) {
      const child = childNodes[i];
      this.deregisterNode(child);
    }

    delete this.nodesMap[node.key];
  }
  remove(data) {
    const node = this.getNode(data);
    if (node) {
      node.parent.removeChild(node);
    }
  }
  append(data, parentData) {
    const parentNode = parentData ? this.getNode(parentData) : this.root;
    if (parentNode) {
      parentNode.insertChild({ data });
    }
  }
  _initDefaultCheckedNodes() {
    const defaultCheckedKeys = this.defaultCheckedKeys || [];
    const nodesMap = this.nodesMap;

    defaultCheckedKeys.forEach(checkedKey => {
      const node = nodesMap[checkedKey];
      if (node) {
        node.setChecked(true, !this.checkStrictly);
        const setChildState = function(node) {
          if (node.childNodes.length === 0) return;
          for (let i = 0, j = node.childNodes.length; i < j; i++) {
            let n = node.childNodes[i];
            if(n.disabled) return;
            n.setChecked(true, false);
            if (n.childNodes.length != 0) {
              setChildState(n);
            }
          }
        };
        setChildState(node);
      }
    });
  }
  _initDefaultCheckedNode(node) {
    const defaultCheckedKeys = this.defaultCheckedKeys || [];
    if (defaultCheckedKeys.indexOf(node.key) !== -1) {
      node.setChecked(true, !this.checkStrictly);
      const setChildState = function(node) {
        if (node.childNodes.length === 0) return;
        for (let i = 0, j = node.childNodes.length; i < j; i++) {
          let n = node.childNodes[i];
          if(n.disabled) return;
          n.setChecked(true, false);
          if (n.childNodes.length != 0) {
            setChildState(n);
          }
        }
      };
      setChildState(node);
    }
  }
  getNode(data) {
    if (data instanceof Node) return data;
    const key = typeof data !== "object" ? data : getNodeKey(this.key, data);
    return this.nodesMap[key] || null;
  }
  loadData(cb) {
    this.root.loadData(cb);
  }
  setData(newVal) {
    const instanceChanged = newVal !== this.root.data;
    if (instanceChanged) {
      this.root.setData(newVal);
      this._initDefaultCheckedNodes();
    } else {
      this.root.updateChildren();
    }
  }
  setCurrentNode(node) {
    this.currentNode = node;
  }
  getCheckedNodes(leafOnly = false) {
    const checkedNodes = [];
    const traverse = function(node) {
      const childNodes = node.root ? node.root.childNodes : node.childNodes;

      childNodes.forEach(child => {
        if (child.checked && (!leafOnly || (leafOnly && child.isLeaf))) {
          checkedNodes.push(child.data);
        }
        traverse(child);
      });
    };
    traverse(this);
    return checkedNodes;
  }
  getCheckedKeys(leafOnly = false) {
    return this.getCheckedNodes(leafOnly).map(data => (data || {})[this.key]);
  }
  _getAllNodes() {
    const allNodes = [];
    const nodesMap = this.nodesMap;
    for (let nodeKey in nodesMap) {
      if (nodesMap.hasOwnProperty(nodeKey)) {
        allNodes.push(nodesMap[nodeKey]);
      }
    }
    return allNodes;
  }
  updateChildren(key, data) {
    const node = this.nodesMap[key];
    if (!node) return;
    const childNodes = node.childNodes;
    for (let i = childNodes.length - 1; i >= 0; i--) {
      const child = childNodes[i];
      this.remove(child.data);
    }
    for (let i = 0, j = data.length; i < j; i++) {
      const child = data[i];
      this.append(child, node.data);
    }
  }
  setChecked(data, checked, deep) {
    const node = this.getNode(data);
    if (node) {
      node.setChecked(!!checked, deep);
    }
  }
  setCheckedNodes(array, leafOnly) {
    const key = this.key;
    const checkedKeys = {};
    array.forEach(item => {
      checkedKeys[(item || {})[key]] = true;
    });
    this._setCheckedKeys(key, leafOnly, checkedKeys);
  }
  setDefaultCheckedKey(newVal){
    if(newVal !== this.defaultCheckedKeys){
      this.defaultCheckedKeys = newVal;
      this._initDefaultCheckedNodes();
    }
  }
  setDefaultExpandedKeys(keys){
    keys = keys || [];
    this.defaultExpandedKeys = keys;

    keys.forEach(key=>{
      const node = this.getNode(key);
      if(node) node.expand(null, this.autoExpandParent);
    })
  }
  _setCheckedKeys(key, leafOnly = false, checkedKeys) {
    const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
    const cache = Object.create(null);
    const keys = Object.keys(checkedKeys);
    allNodes.forEach(node => node.setChecked(false, false));
    for (let i = 0, j = allNodes.length; i < j; i++) {
      const node = allNodes[i];
      const nodeKey = node.data[key].toString();
      let checked = keys.indexOf(nodeKey) > -1;
      if (!checked) {
        if (node.checked && !cache[nodeKey]) {
          node.setChecked(false, false);
        }
        continue;
      }
      let parent = node.parent;
      while (parent && parent.level > 0) {
        cache[parent.data[key]] = true;
        parent = parent.parent;
      }
      if (node.isLeaf || this.checkStrictly) {
        node.setChecked(true, false);
        continue;
      }
      node.setChecked(true, true);

      if (leafOnly) {
        node.setChecked(false, false);
        const traverse = function(node) {
          const childNodes = node.childNodes;
          childNodes.forEach(child => {
            if (!child.isLeaf) {
              child.setChecked(false, false);
            }
            traverse(child);
          });
        };
        traverse(node);
      }
    }
  }
}
